#include "VEnginePCH.h"
#include "VFont.h"
#include "VFTFormat.h"


#define VFT_MAGIC 0x32544656

struct VFontGlyphData
{
	BYTE* Data;
};

class VFontGlyphInterface
{
public:
	VFontGlyphInterface():m_NumChars(0),m_CharInfos(0),m_FontGlyphs(0)
	{
		memset(m_CharRemap, 0, sizeof(m_CharRemap));
	}
	~VFontGlyphInterface()
	{
		Destroy();
	}
	inline void Destroy()
	{
		if (m_CharInfos)
		{
			VFree(m_CharInfos);
			m_CharInfos = NULL;
		}
		if (m_FontGlyphs)
		{
			for (WORD Index = 0; Index < m_NumChars; ++Index)
			{
				if (m_FontGlyphs[Index])
				{
					VFree(m_FontGlyphs[Index]);
				}
			}
			VFree(m_FontGlyphs);
			m_FontGlyphs = NULL;
		}
	}

	BOOL LoadFont(const char* FileName)
	{
		VFileStream* File = VOpenFile(FileName, VOSM_READ);
		if (!File)
		{
			return FALSE;
		}
		UINT FontDataSize = File->GetSize();
		BYTE* FontData = (BYTE*)VMalloc(FontDataSize);
		if (!FontData)
		{
			File->Release();
			return FALSE;
		}
		if (File->Read(FontData, FontDataSize) != FontDataSize)
		{
			VFree(FontData);
			File->Release();
			return FALSE;
		}
		File->Release();
		BOOL Ret = LoadFontFromMemory(FontData, FontDataSize);
		if (!Ret)
		{
			VFree(FontData); 
			return FALSE;
		}
		VFree(FontData);
		return TRUE;
	}
	BOOL LoadFontFromMemory(const void* Data, size_t Size)
	{
		VMemoryStream MemStream((BYTE*)Data, Size);
		VFT_HEADER Header;
		if (MemStream.Read(&Header, sizeof(VFT_HEADER)) != sizeof(VFT_HEADER))
		{
			return FALSE;
		}
		if (Header.Magic != VFT_MAGIC)
		{
			return FALSE;
		}
		m_NumChars = (WORD)Header.NumChars;
		m_Height = Header.Height;
		m_BaseLine = Header.BaseLine;
		// load remap table
		if (MemStream.Read(m_CharRemap, sizeof(m_CharRemap)) != sizeof(m_CharRemap))
		{
			return FALSE;
		}
		// load char infos.
		if (!m_NumChars)
		{
			return FALSE;
		}
		m_CharInfos = (VFontCharInfo*)VMalloc(sizeof(VFontCharInfo) * m_NumChars);
		if (!m_CharInfos)
		{
			return FALSE;
		}
		VFT_CHARINFO TempCharInfo;
		for (WORD Index = 0; Index < m_NumChars; ++Index)
		{
			VFontCharInfo& CharInfo = m_CharInfos[Index];
			MemStream.Read(&TempCharInfo, sizeof(VFT_CHARINFO));
			CharInfo.Width = TempCharInfo.Width;
			CharInfo.Height = TempCharInfo.Height;
			CharInfo.XIncrement = TempCharInfo.XIncrement;
			CharInfo.XOrigin = TempCharInfo.XOrigin;
			CharInfo.YOrigin = TempCharInfo.YOrigin;
			//
			CharInfo.bitmapIndex = 0xFF;
			CharInfo.TexOfsX = 0;
			CharInfo.TexOfsY = 0;
		}
		// load glyphs
		m_FontGlyphs = (BYTE**)VMalloc(sizeof(BYTE*) * m_NumChars);
		if (!m_FontGlyphs)
		{
			return FALSE;
		}
		memset(m_FontGlyphs,0,sizeof(BYTE*) * m_NumChars);
		WORD CharIndex;
		UINT GlyphSize;
		for (WORD Index = 0; Index < 0xFFFF; ++Index)
		{
			CharIndex = m_CharRemap[Index];
			if (CharIndex != 0xFFFF)
			{
				VFontCharInfo& CharInfo = m_CharInfos[CharIndex];
				GlyphSize = CharInfo.Width;
				GlyphSize *= CharInfo.Height;
				if(GlyphSize)
				{
					m_FontGlyphs[CharIndex] = (BYTE*)VMalloc(GlyphSize);
					MemStream.Read(m_FontGlyphs[CharIndex], GlyphSize);
				}
			}
		}
		return TRUE;
	}
	inline WORD RemapChar(wchar_t Char) const
	{
		return m_CharRemap[Char];
	}
	inline VFontCharInfo& GetCharInfo(WORD Index)
	{
		return m_CharInfos[Index];
	}
	inline const BYTE* GetCharGlyph(WORD Index) const
	{
		return m_FontGlyphs[Index];
	}
	inline int GetHeight() const { return m_Height; }
	inline int GetBaseLine() const { return m_BaseLine; }
private:
	WORD m_CharRemap[0xFFFF];		// 128 KB.
	WORD m_NumChars;
	WORD m_BaseLine;
	int m_Height;
	VFontCharInfo* m_CharInfos;
	BYTE** m_FontGlyphs;
};



VFont::VFont(const VName& FaceName, int PageSize):m_GlyphInterface(NULL),m_Height(0),m_BaseLine(0),m_SpaceXIncrement(6)
{
	m_FaceName = FaceName;
	m_GlyphInterface = new VFontGlyphInterface;
	m_Layout.PageSize = PageSize;
}

VFont::~VFont(void)
{
	delete m_GlyphInterface;
}

int VFont::GetStrWidth(const wchar_t* str, int len) 
{
	if (str == NULL || str[0] == 0 || len == 0)   
		return 0;
	
	if (len < 0)
	{
		len = wcslen(str);
		if (len == 0)
		{
			return 0;
		}
	}

	int totWidth = 0;
	WCHAR curChar;
	int charCount;
	const VFontCharInfo* CharInfo = NULL;
	for(charCount = 0; charCount < len; charCount++)
	{
		curChar = str[charCount];
		if(!curChar)
			break;
		
		CharInfo = GetCharInfo(curChar);
		if (CharInfo)
		{
			totWidth += (int)CharInfo->XIncrement;
		}else
		{
			totWidth += m_SpaceXIncrement;
		}
	}

	return totWidth;
}

const VFontCharInfo* VFont::GetCharInfo(wchar_t Char)
{
	WORD CharIndex = m_GlyphInterface->RemapChar(Char);
	if (CharIndex == 0xFFFF)
	{
		return NULL;
	}
	VFontCharInfo& CharInfo = m_GlyphInterface->GetCharInfo(CharIndex);
	if (CharInfo.bitmapIndex == 0xFF)
	{
		const BYTE* Glyph = m_GlyphInterface->GetCharGlyph(CharIndex);
		if (Glyph)
		{
			LayoutCharGlyph(CharInfo, Glyph);
			VASSERT(CharInfo.bitmapIndex != 0xFF);
		}
	}
	return &CharInfo;
}

BOOL VFont::LoadFont(const char* FontFile)
{
	VASSERT(m_Height == 0);
	if (!m_GlyphInterface->LoadFont(FontFile))
	{
		return FALSE;
	}
	m_Height = m_GlyphInterface->GetHeight();
	m_BaseLine = m_GlyphInterface->GetBaseLine();

	const VFontCharInfo* CharInfo = GetCharInfo(L' ');
	if (CharInfo)
	{
		m_SpaceXIncrement = (int)CharInfo->XIncrement;
	}
	
	return TRUE;
}

BOOL VFont::LayoutCharGlyph(VFontCharInfo& CharInfo, const void* Glyph)
{
	UINT NextCurX = (UINT)(m_Layout.CurX + CharInfo.Width ); 
	UINT NextCurY = (UINT)(m_Layout.CurY + m_Height);
	
	// check has space for char .
	if(m_Layout.CurPage == -1 || NextCurY >= (UINT)m_Layout.PageSize)
	{
		m_Layout.CurPage =CreateNewPage();
		m_Layout.CurX = 0;
		m_Layout.CurY = 0;
	
		// Recalc our nexts.
		NextCurX = (UINT)(m_Layout.CurX + CharInfo.Width);
		NextCurY = (UINT)(m_Layout.CurY + m_Height);
	}
	
	// new line
	if( NextCurX >= (UINT)m_Layout.PageSize)
	{
		m_Layout.CurX = 0;
		m_Layout.CurY = NextCurY;
		// Recalc our nexts.
		NextCurX = (UINT)(m_Layout.CurX + CharInfo.Width);
		NextCurY = (UINT)(m_Layout.CurY + m_Height);
	}

	// Check the Y once more - sometimes we advance to a new row and run off
	// the end.
	if(NextCurY >= (UINT)m_Layout.PageSize)
	{
		m_Layout.CurPage = CreateNewPage();
		m_Layout.CurX = 0;
		m_Layout.CurY = 0;

		// Recalc our nexts.
		NextCurX = (UINT)(m_Layout.CurX + CharInfo.Width);
		NextCurY = (UINT)(m_Layout.CurY + m_Height);
	}

	CharInfo.bitmapIndex = m_Layout.CurPage;
	CharInfo.TexOfsX = (WORD)m_Layout.CurX;
	CharInfo.TexOfsY = (WORD)m_Layout.CurY;

	m_Layout.CurX = NextCurX;
	
	// update surface 
	if (CharInfo.Width > 0 && CharInfo.Height > 0)
	{
		FillCharGlyph(CharInfo, Glyph);
		//int x, y;

		//// hack way.
		//IDirect3DTexture9* pSurface = (IDirect3DTexture9*)m_FontSurface[m_nCurSheet];
		//VASSERT(pSurface);

		//D3DLOCKED_RECT LockedRect;
		//RECT rcToLock;
		//rcToLock.left = charInfo.xOffset;
		//rcToLock.top = charInfo.yOffset;
		//rcToLock.right = charInfo.xOffset + charInfo.width /*+ CharPaddedWidth*/;
		//rcToLock.bottom = charInfo.yOffset + charInfo.height/*+ CharPaddedHeight*/;
		//if (FAILED(pSurface->LockRect(0, &LockedRect, &rcToLock, 0)))
		//{
		//	return;
		//}
		//DWORD* Dest = (DWORD*)LockedRect.pBits;
		//UINT Pitch = LockedRect.Pitch >> 2;
		//for(y = 0;y < charInfo.height;y++)
		//{
		//	for(x = 0;x < charInfo.width;x++)
		//	{
		//		Dest[x + y*Pitch] = glyph.pPixels[y][x];
		//	}
		//}

		//pSurface->UnlockRect(0);
	}


#if 0
	char szBuf[MAX_PATH];
	QSprintf(szBuf,"Font_Texture_%d.bmp", m_nCurSheet);
	D3DXSaveTextureToFile(szBuf, D3DXIFF_BMP, pSurface, NULL);
#endif 

	return TRUE;
}

//BOOL VFont::CreateNewPage()
//{
//	VRTexturePtr NewFontPage = VRI->CreateTexture(m_Layout.PageSize, m_Layout.PageSize, VRPF_A8);
//	VASSERT(NewFontPage);
//	m_FontTextures.push_back(NewFontPage);
//	NewFontPage->AddRef();
//
//	return TRUE;
//}